//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections;
using System.Collections.Generic;

namespace OpenLS.Spreadsheet.Expressions
{
    internal class PrefixUnaryExpression : UnaryExpressionBase
    {
        private readonly UnaryOperatorType _operator;

        public PrefixUnaryExpression(UnaryOperatorType op, Expression exp) : base(exp)
        {
            _operator = op;
        }

        public UnaryOperatorType Operator
        {
            get { return _operator; }
        }

        public override Expression Offset(CellRelativeAddress a)
        {
            return new PrefixUnaryExpression(Operator, Inner.Offset(a));
        }
        internal override Expression Transposed(Cell target)
        {
            return new PrefixUnaryExpression(Operator, this.Inner.Transposed(target));
        }

        public override string GetText(TextContext context)
        {
            string t = Inner.GetText(context);
            switch (Operator)
            {
                case UnaryOperatorType.Add:
                    return "+" + t;
                case UnaryOperatorType.Subtract:
                    return "-" + t;
                default:
                    throw new InvalidOperationException();
            }
        }

        protected override object GetObjectImp(EvaluationContext context)
        {
            object val = Inner.GetValue(context) ?? 0.0d;
            if (val is IArray)
                return new UnaryExpresionArray(this.Operator, context, (IArray) val);
            return evaluate(Operator, context, val);
        }

        private static object evaluate(UnaryOperatorType operatorType, EvaluationContext context, object val)
        {
            if (val is Error)
                return val;
            if (val is DateTime)
                val = context.Worksheet.Workbook.DateTimeToDouble((DateTime) val);
            switch (operatorType)
            {
                case UnaryOperatorType.Add:
                    return val;
                case UnaryOperatorType.Subtract:
                    double d = Convert.ToDouble(val);
                    return -d;
                default:
                    throw new InvalidOperationException();
            }
        }

        #region Nested type: UnaryExpresionArray

        private class UnaryExpresionArray : IArray
        {
            private readonly UnaryOperatorType _operatorType;
            private readonly EvaluationContext _context;
            private readonly IArray _array;

            public UnaryExpresionArray(UnaryOperatorType operatorType, EvaluationContext context,  IArray array)
            {
                _operatorType = operatorType;
                _context = context;
                _array = array;
            }

            #region IArray Members

            public IEnumerator<object> GetEnumerator()
            {
                foreach (var item in _array)
                {
                    yield return evaluate(_operatorType, _context, item);
                }
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }

            public object this[int row, int column]
            {
                get { return evaluate(_operatorType, _context, ArrayExpressionHelper.AccessCell( _array[row, column])); }
            }

            public int RowCount
            {
                get { return _array.RowCount; }
            }

            public int ColumnCount
            {
                get { return _array.ColumnCount; }
            }

            #endregion
        }

        #endregion
    }
}